home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
Libraries
/
Sherlock 2.0
/
DevLibSrc
/
Main_DevLib
/
LIBobj.c
< prev
next >
Wrap
Text File
|
1995-11-16
|
16KB
|
717 lines
/*
devlib: Memory allocation routines.
source: LIBobj.c
started: November 4, 1993.
version:
November 7, 1995.
Replace sprintf with cvt_l2s.
February 25, 1995.
Removed spurious code from obj_check_fill
December 26, 1993.
Bug fix: change obj_find_type to correctly alphabetize types.
Added variable width columns in obj_dump_stats.
December 9, 1993.
Added obj_max_tag_size logic.
November 6, 1993.
Bug fix (!) to obj_find_type.
Commented out obj_free.
*/
#include <LIBlib.h>
#include <LIBend.h>
#include <LIBenv.h>
#include <LIBmem.h>
#include <LIBobj.h>
#include <stdlib.h>
#include <string.h>
#define PRODUCTION_TRACE
#undef PRODUCTION_TRACE
#ifdef TURBOC
#include <alloc.h>
#endif
#ifdef MICRO_SOFT
#include <malloc.h>
#endif
/*
Only obj_new() is defined in the production version.
*/
#ifndef PRODUCTION
/*
Convert an object pointer to a header pointer.
*/
#define p2h(p) ((obj_head *) (((char *) p) - sizeof(obj_head)))
/*
Define the list of all objects.
This list is kept in reverse allocation order.
The obj_olist field of obj_head_struct holds the links of this list.
*/
static obj_head * obj_all;
/*
Define global cumulative statistics.
*/
static long obj_ntypes; /* Number of types. */
static long obj_num_strcmp = 0; /* Number of calls to strcmp in obj_find_type. */
static size_t obj_max_tag_size = 0; /* Length of longest type tag. */
static long obj_nmax; /* Object size statistics. */
static long obj_nnet;
static long obj_ntot;
static long obj_cmax; /* Alloc/free (call) statistics. */
static long obj_cnet;
static long obj_ctot;
/*
Define the alphabetical list of type descriptors.
The type_alpha field of the type descriptor holds the links of this list.
*/
static type_des * obj_alpha = NULL;
/*
Prototypes of internal routines.
*/
static void obj_check_fill (char *p, long n, char * dtag);
static void obj_dump_fill (char *p, int n, char * message);
static void obj_dump_header (obj_head *h, char * pad_string);
static void obj_dump_obj_header (void *p, char * pad_string);
static type_des * obj_find_type (char * tag);
static void obj_free_check (void * obj);
/*
Check the list of all known objects for consistency.
*/
void
obj_check_all(void)
{
FTAG("obj_check_all");
type_des * t;
obj_head * h;
STATB(ftag);
for (t = obj_alpha; t != NULL; t = t -> type_alpha) {
int attrib = t -> type_attrib;
long sum = 0;
int number = 0;
for (h = t -> type_tlist; h != NULL; h = h -> obj_tlist) {
obj_check_fill( ((char *) h + sizeof(obj_head)),
h -> obj_size, h -> obj_dtag);
sum += h -> obj_size;
number++;
}
ASSERT_TRACE(sum == t -> type_nnet,
es(ftag); es(":"); es(current_ftag); es(" :type: "); es(t -> type_name);
enl();
es("sum: "); elong(sum);
es(" != type_nnet: "); elong(t -> type_nnet);
);
ASSERT_TRACE(number == t -> type_cnet,
es(ftag); es(":"); es(current_ftag); es(t -> type_name);
enl();
es("number: "); elong(number);
es(" != type_cnet"); elong(t -> type_cnet);
);
}
STATX(ftag);
}
/*
Check the header and trailer areas.
n is the size of the block *not* counting the header and trailer.
p is a pointer to the memory block, not the header.
*/
static void
obj_check_fill(char * p, long n, char * dtag)
{
FTAG("obj_check_fill");
obj_head * h = p2h(p);
char * q;
int i;
unsigned int val;
STATB(ftag);
/* Check the header. */
for(i = 0; i < OBJ_HEADER_SIZE; i++) {
val = (h -> obj_prot[i]) & 0xff;
ASSERT_TRACE(val == OBJ_FILLER,
obj_dump_fill(&(h->obj_prot[0]), OBJ_HEADER_SIZE, "Bad header\n");
es(ftag); es(":"); es(current_ftag); es(":"); es(dtag); enl();
es(" p: "); eptr(p);
es(" n: "); elong(n);
es(" header byte "); eint(i); enl();
obj_dump_header(h, "");
);
}
/* Check the trailer. */
for (q = p + n, i = 0; i < OBJ_TRAILER_SIZE; i++, q++) {
val = (*q) & 0xff;
ASSERT_TRACE(val == OBJ_FILLER,
obj_dump_fill(p + n, OBJ_TRAILER_SIZE, "Bad trailer\n");
es(ftag); es(":"); es(current_ftag); es(":"); es(dtag); enl();
es(" p: "); eptr(p);
es(" n: "); elong(n);
es(" trailer byte "); eint(i); enl();
obj_dump_header(h, "");
);
}
STATX(ftag);
}
/*
Make sure obj is a valid object.
*/
void
obj_check_object(void * obj)
{
FTAG("obj_check_object");
obj_head * h;
obj_head * h2 = p2h(obj);
STATB(ftag);
for (h = obj_all; h != NULL; h = h -> obj_olist) {
/* Found. */
if (h == h2) {
/* Check the header and trailer bytes. */
obj_check_fill( ((char *) h + sizeof(obj_head)),
h -> obj_size, h -> obj_dtag);
STATX(ftag);
return;
}
}
/* Not found. */
{
char buf [100];
err_fatal2("unallocated pointer: ", cvt_ptr(buf, 100, obj));
}
}
/*
Print the fill area.
*/
static void
obj_dump_fill(char *p, int n, char * message)
{
FTAG("obj_dump_fill");
int i;
unsigned int val;
STATB(ftag);
ecnl(); enl(); es(message); enl();
for (i = 0; i < n; i++) {
val = (p[i]) & 0xff;
if (val == OBJ_FILLER) {
es("<FILL>");
}
else {
echar(p[i]); es("<"); ehexchar(p[i]); es(">");
}
}
enl();
STATX(ftag);
}
/*
Dump the header of an object, given a pointer to the header.
*/
static void
obj_dump_header(obj_head * h, char * pad_string)
{
FTAG("obj_dump_header");
char * p = (char *) (((char *) h) + sizeof(obj_head));
type_des * t = h -> obj_type;
int attrib = t -> type_attrib;
STATB(ftag);
ASSERT(t -> type_name);
/* Preceding a type name with a minus sign inhibits this dump. */
if (t -> type_name[0] != '-') {
es(pad_string);
es("p: "); eptr(p);
es(" n: "); epadint(h -> obj_size, 5);
es(" "); es(h -> obj_dtag);
ecnl();
}
STATX(ftag);
}
/*
Dump all objects that have not been freed.
*/
void
obj_dump_objects(void)
{
FTAG("obj_dump_objects");
STATB(ftag);
{
register obj_head * h = obj_all;
while (h) {
obj_dump_header(h, "");
h = h -> obj_olist;
}
}
STATX(ftag);
}
/*
Dump the value of a single header, given a pointer to the object.
*/
static void
obj_dump_obj_header(void * obj, char * pad_string)
{
FTAG("obj_dump_obj_header");
STATB(ftag);
obj_dump_header(p2h(obj), pad_string);
STATX(ftag);
}
/*
Print all statistics alphabetically by type name.
*/
void
obj_dump_stats(void)
{
FTAG("obj_dump_stats");
register type_des * t;
long ntot_width = 7; /* These are the minimum widths of the columns. */
long nmax_width = 7;
long nnet_width = 7;
long ctot_width = 7;
long cmax_width = 7;
long cnet_width = 7;
char buf [CVT_BUF_SIZE];
STATB(ftag);
/* Determine the widths of all columns. */
obj_max_tag_size = max(10, obj_max_tag_size);
#if 0 /* Avoid sprintf so we don't pull in the whole floating library. */
ntot_width = max(ntot_width, 1 + sprintf(buf, "%ld", obj_ntot));
nmax_width = max(nmax_width, 1 + sprintf(buf, "%ld", obj_nmax));
nnet_width = max(nnet_width, 1 + sprintf(buf, "%ld", obj_nnet));
ctot_width = max(ctot_width, 1 + sprintf(buf, "%ld", obj_ctot));
cmax_width = max(cmax_width, 1 + sprintf(buf, "%ld", obj_cmax));
cnet_width = max(cnet_width, 1 + sprintf(buf, "%ld", obj_cnet));
#else
ntot_width = max(ntot_width, 1 + cvt_l2s(buf, obj_ntot));
nmax_width = max(nmax_width, 1 + cvt_l2s(buf, obj_nmax));
nnet_width = max(nnet_width, 1 + cvt_l2s(buf, obj_nnet));
ctot_width = max(ctot_width, 1 + cvt_l2s(buf, obj_ctot));
cmax_width = max(cmax_width, 1 + cvt_l2s(buf, obj_cmax));
cnet_width = max(cnet_width, 1 + cvt_l2s(buf, obj_cnet));
#endif
ecnls(2);
#if 0 /* Not used since these are user statistics. */
es(title); es(": Class statistics:\n\n");
#endif
epads("TYPE NAME", obj_max_tag_size);
epads("ntot", ntot_width); epads("nmax", nmax_width); epads("nnet", nnet_width);
epads("ctot", ctot_width); epads("cmax", cmax_width); epads("cnet", cnet_width);
enl();
for (t = obj_alpha; t != NULL; t = t -> type_alpha) {
epads(t -> type_name, obj_max_tag_size);
epadlong(t -> type_ntot, ntot_width);
epadlong(t -> type_nmax, nmax_width);
epadlong(t -> type_nnet, nnet_width);
epadlong(t -> type_ctot, ctot_width);
epadlong(t -> type_cmax, cmax_width);
epadlong(t -> type_cnet, cnet_width);
enl();
}
epads("TOTALS", obj_max_tag_size);
epadlong(obj_ntot, ntot_width);
epadlong(obj_nmax, nmax_width);
epadlong(obj_nnet, nnet_width);
epadlong(obj_ctot, ctot_width);
epadlong(obj_cmax, cmax_width);
epadlong(obj_cnet, cnet_width);
enl();
epads(" ", obj_max_tag_size);
epads("ntot", ntot_width); epads("nmax", nmax_width); epads("nnet", nnet_width);
epads("ctot", ctot_width); epads("cmax", cmax_width); epads("cnet", cnet_width);
enl();
#if 0 /* Not used since these are user statistics. */
es("obj_ntypes: "); elong(obj_ntypes); enl();
es("obj_num_strcmp: "); elong(obj_num_strcmp); enl();
#endif
obj_check_all();
STATX(ftag);
}
/*
Find the type descriptor corresponding to a tag.
Create a new type descriptor if necessary.
This lookup may be very slow because this routine is only called once
per each instance of a call to obj_new_db in the text.
*/
static type_des *
obj_find_type (char * tag)
{
FTAG("obj_find_type");
register type_des * t = NULL;
register type_des * t_prev = NULL;
STATB(ftag);
/* Search the alphabetical list of types. */
for (t_prev = NULL, t = obj_alpha; t; t_prev = t, t = t -> type_alpha) {
int n = strcmp(tag, t -> type_name);
obj_num_strcmp++;
if (n == 0) {
goto found;
}
if (n < 0) {
goto not_found;
}
}
not_found:
/* Set obj_max_tag_size */
{
size_t tag_size = strlen(tag);
obj_max_tag_size = max(tag_size, obj_max_tag_size);
}
/* Create a new type descriptor. */
t = obj_new(sizeof(type_des));
t -> type_name = tag;
t -> type_magic = OBJ_MAGIC;
obj_ntypes++;
/* Link the type descriptor into the alphabetical list. */
if (t_prev == NULL) {
/* Link it before the first node. */
t -> type_alpha = obj_alpha; /* Bug fix: 11/6/93 */
obj_alpha = t;
}
else {
ASSERT(obj_alpha);
t -> type_alpha = t_prev -> type_alpha;
t_prev -> type_alpha = t;
}
TRACEPX(ftag, es(" name: "); es(tag); enl());
return t;
found:
STATX(ftag);
return t;
}
/*
Check to make sure the object is on the object list for its type.
If found:
• remove the object from the list of allocated nodes.
• Update the net statistics for the object.
*/
static void
obj_free_check(void * p)
{
FTAG("obj_free_check");
obj_head * h, * h2, * h3;
type_des * t;
STATB(ftag);
ASSERT_TRACE(p, es(": NULL object"));
h = p2h(p);
t = h -> obj_type;
ASSERT_TRACE(t, es(": NULL type"); obj_dump_obj_header(p, "*"));
ASSERT_TRACE(obj_all, es(": NULL obj_all"));
/* Remove the node from the list of all allocated nodes. */
if (obj_all == h) {
obj_all = h -> obj_olist;
goto found1;
}
for ( h2 = obj_all, h3 = h2 -> obj_olist;
h3 != NULL;
h2 = h3, h3 = h2 -> obj_olist
) {
if (h3 == h) {
/* Remove h3. */
h2 -> obj_olist = h3 -> obj_olist;
goto found1;
}
}
/*
It is often useful to pretend it *is* on the list for dump purposes.
*/
{
char buf [100];
err_fatal2("object not on object list: ", cvt_ptr(buf, 100, p));
}
found1:
ASSERT_TRACE(t -> type_tlist,
es(": NULL type_tlist");
obj_dump_obj_header(p, "*");
);
/* Remove the node from the list of nodes of a particular type. */
if (t -> type_tlist == h) {
t -> type_tlist = h -> obj_tlist;
goto found2;
}
for ( h2 = t -> type_tlist, h3 = h2 -> obj_tlist;
h3 != NULL;
h2 = h3, h3 = h2 -> obj_tlist
) {
if (h3 == h) {
/* Remove h3. */
h2 -> obj_tlist = h3 -> obj_tlist;
goto found2;
}
}
{
char buf [100];
err_fatal2("object not on type's object list: ", cvt_ptr(buf,100, p));
}
found2:
/* Reduce the net statistics. */
t -> type_nnet -= h -> obj_size;
obj_nnet -= h -> obj_size;
t -> type_cnet--;
obj_cnet--;
STATX(ftag);
}
/*
Free a block of memory allocated by obj_alloc.
p must point to the logical block, *not* the header.
*/
void
obj_free_db(void * obj)
{
FTAG("obj_free_db");
obj_head * h = p2h(obj);
STATB(ftag);
ASSERT(obj);
obj_free_check(obj);
TRACE("obj_v", obj_check_all());
/*
free() may "corrupt" the object;
make sure to print the object *before* freeing it.
*/
TRACEP(ftag, obj_dump_obj_header(obj, "*"));
TRACEN("-obj_watch",
sl_unwatch(&(h -> obj_prot[0]), OBJ_HEADER_SIZE);
sl_unwatch( ((char *)obj) + (h -> obj_size), OBJ_TRAILER_SIZE);
);
lib_free(h);
TRACEPX(ftag, es("lib_free("); eptr(h); es(")\n"));
}
#endif /* n PRODUCTION */
/*
Return a block of zeroed memory and abort if not enough memory is available.
*/
void *
obj_new(size_t n)
{
FTAG("obj_new");
char * p;
STATB(ftag);
/* Align the request. */
if (n & 1) {
n++;
}
p = lib_calloc((size_t) 1, (size_t) n);
#ifdef PRODUCTION_TRACE
ecnl(); es(ftag);
es(" n: "); elong(n);
es(" p: "); eptr(p); enl();
#endif
if (p == NULL) {
char buf [CVT_BUF_SIZE];
err_fatal3(
"Sorry, can not allocate ",
cvt_int(buf, CVT_BUF_SIZE, n),
" bytes");
}
TRACEPX(ftag, eret(); eptr(p); es(" = lib_calloc(1, "); elong(n); es(")\n"));
return p;
}
/*
Debugging version of obj_new.
*/
#ifndef PRODUCTION
void *
obj_new_db(size_t n, char * tag, type_des ** tp)
{
FTAG("obj_new_db");
register char * p; /* Pointer to logical object. */
register obj_head * h; /* Pointer to object header. */
register type_des * t; /* Pointer to object's type descriptor. */
STATB(ftag);
/* All fields are protected. */
h = obj_new(n + sizeof(obj_head) + OBJ_TRAILER_SIZE);
p = (((char *) h) + sizeof(obj_head));
if (*tp == NULL) {
*tp = obj_find_type(tag);
}
t = *tp;
/* Fill in the fields of the header. */
h -> obj_type = t;
h -> obj_dtag = tag;
h -> obj_tlist = t -> type_tlist;
t -> type_tlist = h;
h -> obj_olist = obj_all;
obj_all = h;
h -> obj_size = n;
/* Update the staticstics fields in the type descriptor. */
t -> type_cnet++;
t -> type_cmax = max(t -> type_cmax, t -> type_cnet);
t -> type_ctot++;
t -> type_nnet += (long) n;
t -> type_nmax = max(t -> type_nmax, t -> type_nnet);
t -> type_ntot += (long) n;
obj_cnet++;
obj_cmax = max(obj_cmax, obj_cnet);
obj_ctot++;
obj_nnet += (long) n;
obj_nmax = max(obj_nmax, obj_nnet);
obj_ntot += (long) n;
/* Fill in the header and trailer protection fields. */
{
register int i;
register char * q;
for(i = 0; i < OBJ_HEADER_SIZE; i++) {
h -> obj_prot[i] = OBJ_FILLER;
}
for (q = p + n, i = 0; i < OBJ_TRAILER_SIZE; i++) {
*q++ = OBJ_FILLER;
}
TRACEN("-obj_watch",
sl_watch(&(h -> obj_prot[0]), OBJ_HEADER_SIZE, "obj header");
sl_watch(p + n, OBJ_TRAILER_SIZE, "obj trailer");
);
}
TRACEN("obj_v", obj_check_all());
TRACEPX(ftag, epadlong(n,4); eblank(); eret(); eptr(p);
eblank(); es(t -> type_name); enl());
return p;
}
/*
Test this module for blunders.
Memory allocation stress test.
*/
#define MAX_IT 50
#define MAX_OBJ 10
#define OBJ_SIZE 50
#define MAX_ITEMS 12
#define ITEM_SIZE 20
#define ITEM_PER_BLOCK 5
static char * objects [MAX_OBJ];
/*
A test to see whether obj_free_db really is freeing memory.
*/
void
obj_test (void)
{
FTAG("obj_test");
FTAGV("-obj_test_v");
register int i, j;
static type_des * otype = NULL;
STATB(ftag);
ecnl(); enl();
es("obj_test: start of allocation test\n\n");
env_dump_stats();
/* Start loops at 1 so we do not request 0 bytes. */
for (i = 1; i < MAX_IT; i++) {
for (j = 1; j < MAX_OBJ; j++) {
type_des * tp = NULL;
objects [j] = obj_new_db(OBJ_SIZE * j * i, "object_test_type", &tp);
}
for (j = 1; j < MAX_OBJ; j++) {
obj_free_db(objects [j]);
}
es("End of iteration "); eint(i); enl();
}
TRACEP(ftagv, sl_off("obj_*"); sl_on("obj_v"));
es("Allocation test complete\n\n");
env_dump_stats();
STATX(ftag);
}
#endif /* n PRODUCTION */